SMU Honors Physics: Adventures in Spacetime

Authors: Stephen Sekula

In this python notebook, we will explore numbers, especially vectors - numbers that define both a length and a direction. Vectors are a crucial class of numbers in physics. Many quantities in the natural world must be specified by more than just a size or a length; they indicate where to go next, and also need to specify directional information. In this unit, we explore simple numbers without direction ("scalars"), then move on to numbers with direction ("vectors"). This will all be done with a programming language, called PYTHON, which you will use over and over this semester to explore different questions about the natural world.

In this unit, you will learn the following:

  • How to define scalars in PYTHON.
  • How to operate on scalars to make them do things, like make other scalars.
  • How to define vectors in general
  • How to define vectors in PYTHON
  • How to operate on vectors
  • How to use vectors to represent physical quantities, like those that describe motion in space and time
  • Apply vectors to model a physical process

Difficulty level of a section

Sections below are marked by one of three graphics: green circle, blue square, and black diamond. If you have ever gone skiing before, you might recognize these as ski trail difficulty markers. "Green circle" indicates a beginner-level trail; "Blue square" is intermediate-level; "Black diamond" is advanced level.

Everyone should be able to make progress on green circle material. Once you have a good comfort level with green circle, blue square will feel much more approachable. Black diamond may challenge even experienced people, but nonetheless it is important to see what is possible and aspire!

  • Green Circle: BEGINNER
  • Blue Square: INTERMEDIATE
  • Black Diamond: ADVANCED

Scalars (in general, and in PYTHON)

A "scalar" is a number with magnitude, but no directions. Here are some physical quantities that are represented by scalars:

  • Your age: "I am 19 years old" is a description of the magnitude of your age. Of course, we all know that time only ever seems to move in one direction, so we never bother to specify the direction of age.
  • The temperature of a room: "It's 70F in this office right now" is a statement about the magnitude of the temperature (heat energy content) of the room. It has no directional information. For instance, knowing that it is 70F in this room doesn't tell us about the temperature in the hall, or the next room. It has no information about what temperature is doing elsewhere.
  • Speed: "I am driving at 70 miles per hour" is a statement about how far you go over a certain amount of time. Nobody can figure out, from just this information, whether you are driving north, south, east, west, or some combination of those. Speed is a scalar that gives no information about what your speed will be later, what it was earlier, or in what direction your motion is aimed.

Now that you have familiarized yourself with scalars in the physical world, let us represent them using the PYTHON programming language and then learn to operate on scalars to do things - like make other scalars.

Scalars in PYTHON - integers and floating point numbers

PYTHON has two built-in simple scalar types: integers (0,1,2,3,... or -1,-2,-3,...) and "floating point" numbers, more commonly known as "decimal numbers" (1.1, 3.14159, 75.008). PYTHON allows you to have a symbol represent a number (like in algebra) and then use the symbol to conduct operations on the numbers. Consider the following first steps into PYTHON to store a decimal number using a "variable" (a symbol that stands for a number and whose value can be altered), and then act on the variable with mathematical operations (addition, +, subtraction, -, multiplication, *, and division, /).

To make the next block of "code" (programming commands) do something, click your mouse cursor into the block and press the SHIFT key and the ENTER key at the same time.


In [1]:
# This is a comment. PYTHON ignores these, but they are helpful for people!
# Any line that begins with a # symbol is a comment.

# Let's define a variable, x:
x = 5.0

# By just placing the variable on its own line, we can print its value:
x

# That's not very pretty. Just a number on a line. Let's print this with some helpful text:
print("The value of x is %f" %(x))

# How about printing a multiplication operation?
print("%f" %(x*5.0))

# We don't need all those decimal places. Let's print just a few (or maybe lots more! Play around!)
print("%.2f" % (x*5.0))

# Let's define a new variable, y, based on x:
y = x*2.0
print("The value of y is:")
print(y)

# Let's print out some arithmetic operations between x and y:
print("Addition")
print(x+y)

print("Subtraction")
print(x-y)

print("Multiplication")
print(x*y)

print("Division")
print(x/y)

# Congratulations - you just learned how to make a computer do whatever you want. Imagine the endless applications!


The value of x is 5.000000
25.000000
25.00
The value of y is:
10.0
Addition
15.0
Subtraction
-5.0
Multiplication
50.0
Division
0.5

Vectors

A "vector" is a collection of scalars that, together, represent both magnitude and direction. Many physical quantities can only be accurately described by vectors. For example:

  • Velocity: "I am driving northeast at 70 miles per hour" is a statement both about direction ("northwest") and magnitude ("70 miles per hour"). Not only do you know how much space is being covered, and in what time it is covered - you know where to find the person at a later time because you know their direction! If you know the starting point, you can keep track of a journey.
  • Acceleration: "I am falling down to the earth and my speed increases by 9.8 meters per second, every second" is a statement about the rate of change of speed, and the direction in which that change occurs. The earth's gravitational force causes such a change in speed near the surface of the earth, and always seems to cause the change in a direction that points toward the center of the earth ("down").
  • Electric force: "The lightning jumped from the storm cloud to the ground" is a statement about how much charge was drained from a storm cloud (in the sky) to the ground (down below the storm). This is a vector, containing both direction (downward) and magnitude (lightning is a number of electric charges that all make the jump). Even without actual motion, electric charges can exert forces on each other and those forces have directionality and strength.

There are many notations for a vector. For instance, let us try to represent the first example above - velocity - using a notation.

"I am driving northeast at 70 miles per hour."

Let us assume that the driver is moving an equal amount of distance north for every amount west that they move. We can represent one step in their motion as follows:


In [2]:
# We'll need this more later, but this code snippet will draw arrows. We need them to represent the above statement
import matplotlib.pyplot as plt
import math
%matplotlib inline
plt.axis('on')
plt.xlim(-1.0,1.0)
plt.ylim(-1.0,1.0)

# Draw an east-pointing arrow
plt.arrow(0, 0, 1/math.sqrt(2)-0.1, 0, head_width=0.05, head_length=0.1, fc='k', ec='k');

# Draw a north-pointing arrow
plt.arrow(1/math.sqrt(2), 0, 0, 1/math.sqrt(2)-0.1, head_width=0.05, head_length=0.1, fc='k', ec='k');

# Draw the actual arrow representing the total motion of the car
plt.arrow(0, 0, 0.9/math.sqrt(2), 0.9/math.sqrt(2), head_width=0.05, head_length=0.1, fc='c', ec='c');


The above code snippet will help us later in doing more vector visualization, but for now focus on notating the above picture. For every bit of distance, x, that we go eastward, we go a distance, y, northward. In the above picture, x=y . . . but that doesn't always have to be the case. In fact, most of the time it's not.

Let us look at two popular notations for the above:

$\vec{v} = (x,y)$

or

$\vec{v} = x\hat{i} + y\hat{j}$

The first notation uses parentheses and commas to separate the horizontal component (x) from the vertical component (y). The second notation uses an additional kind of vector, a unit vector, to represent "direction along horizontal" ($\hat{i}$) and "direction along vertical" ($\hat{j}$).

For this exercise, we prefer the first notation since it's very similar to what we will do in PYTHON to represent vectors. Let's look at that.

Mathematical Operations on Vectors

  • Adding two vectors is as simple as adding the like-components: the first component in each vector is added to the first component in the next; the second component in each vector is added to the second component of the next; etc. Mathematically, we can represent this as follows: ${\vec v}_1 + {\vec v}_2 = (1,5) + (4,5) = (1+4, 5+5) = (5,10)$. Note that the sum of two vectors yields a new vector.
  • Subtracting two vectors is just like addition, but with minus signs. For example, ${\vec v}_1 - {\vec v}_2 = (1,5)-(4,5) = (1-4, 5-5) = (-3, 0)$. Again, subtraction yields a new vector.
  • Scalar multiplication involves multiplying a vector by a scalar. In this case, you change the overall length of the vector, but not its direction (we will see this in PYTHON below). For example, let $a=5$ and ${\vec v} = (1,5)$, and then multiply them: $a{\vec v} = 5(1,5) = (5 \cdot 1, 5 \cdot 5) = (5,25)$. Note that scalar multiplication yields a vector.
  • Dot-product vector multiplication: one way to multiply two vectors is to take the "dot product," in which you multiply each component by the same component from another vector and sum the resulting products. For example: ${\vec v}_1 \cdot {\vec v}_2 = (1,5) \cdot (4,5) = (1 \cdot 4) + (5 \cdot 5) = 4 + 25 = 29$. Note that the dot-product of two vectors is a scalar - a pure number.

The Length of a Vector

A vector is an arrow. It has direction, but also length ("magnitude"). We now have enough mathematical information to calculate the length of any vector, knowing its components.

We can determine the length of a vector using the Pythagorean Theorem. Consider the picture above of driving east and then driving north. The eastward movement creates a horizontal vector. The northward movement creates a vertical vector. The resulting total motion - driving northeast - is the sum of two independent motions, one eastward and one northward. We can write the eastward vector (of length $e$) as:

\begin{equation} \vec{e} = (e,0) \end{equation}

and the northward vector (of length $n$) as:

\begin{equation} \vec{n} = (0,n) \end{equation}

If we add them, using the rules above, we arrive at the total motion vector:

\begin{equation} \vec{v} = (e,0) + (0,n) = (e,n). \end{equation}

But what is the length of this total vector? The two components form the base and height if a right-triangle. You can imagine, then, that if we know the length of the base and the length of the height, we can find the total length of the hypotenuse - which is just the length ($L$) of the total vector! This would look like this:

\begin{equation} L^2 = e^2 + n^2 \longrightarrow L = \sqrt{e^2 + n^2} \end{equation}

Consider the mathematical operations we introduced above. Which operation does the length-squared, $L^2$, resemble?

If you said "dot-product," you're correct. In fact, the dot-product of a vector with its own self gives you the square of the length of that vector:

\begin{equation} {\vec v} \cdot {\vec v} = (e,n) \cdot (e,n) = (e \cdot e) + (n \cdot n) = e^2 + n^2 = L^2 \end{equation}

Let us now play with all of this in PYTHON.

Vectors in PYTHON

Below, we show you how to write vectors using PYTHON and then act on vectors with various algebraic operations. Play around with these and see if you can get a feel for what it means to define and then manipulate a vector. The visualization below will update when you make changes.


In [3]:
# import a library of numerical tools for defining and manipulating vectors
import numpy as np

# Let's define a vector using two scalar variables to set the components
x = 1.0
y = 5.0

v_1 = np.array([x,y])

# Let us print the components of the vector
print("The components of the vector, v_1, are as follows:")
print("x = %f" % (v_1[0]))
print("y = %f" % (v_1[1]))

# We see that adding the operator [] after the vector, with a number inside the square brackets, gains us
# access to the components of the vector.

# Let us define a second vector
v_2 = np.array([4,5])

# Let us do vector addition:
print("Vector addition of v_1 + v_2 yields: (%f, %f)" % ((v_1+v_2)[0], (v_1+v_2)[1]))

# We see that this, indeed, yields what we expect from the definition discussed above!
# Let us subtract them:
print("Vector subtraction of v_1 - v_2 yields: (%f, %f)" % ((v_1-v_2)[0], (v_1-v_2)[1]))

# Let us now multiply the vector by a scalar
a = 5
print("Scalar multiplication of a * v_1 yields: (%f, %f)" % ((a*v_1)[0],(a*v_1)[1]))

# Just as we expected from the definitions above!

# Let us now do the dot-product multiplication of two vectors.
# Numpy provides a special "function" to execute the dot product:
print("Dot-product multiplication of v_1 * v_2 yields: %f" % (np.dot(v_1, v_2)))

# Again, this gives us what we expected from the definition of the dot product.

# Let us now calculate the length of the vector, v_1, in two ways.

# The first way is "brute-force" - do the dot-product multiplication and then square-root it:
length = math.sqrt( np.dot(v_1, v_1))
print("Length Method A: the length of v_1 is %f" % (length))

# The second way is to use numpy's built-in function for computing the length:
length = np.linalg.norm(v_1)
print("Length Method B: the length of v_1 is %f" % (length))

# Oh, look. They are the same!
# Method B utilizes the "Linear Algebra" tools, and specifically the "norm()" function, which is
# designed to return the length of a vector of any size (we used a 2-dimensional vector, but you
# could give it a 10-dimensional vector and it still works!). "Linear Algebra" is a mathematics class
# you might take later, and it's the algebra of structured numbers like vectors and matrices, generically
# known as "tensors".

# Here is a 5-dimensional vector:
v_3 = np.array([1,3,5,7,9])
print("The length of our 5-D vector is: %f" % (np.linalg.norm(v_3)))

# Neat, huh? Try computing the length yourself and see if the above answer is right.


The components of the vector, v_1, are as follows:
x = 1.000000
y = 5.000000
Vector addition of v_1 + v_2 yields: (5.000000, 10.000000)
Vector subtraction of v_1 - v_2 yields: (-3.000000, 0.000000)
Scalar multiplication of a * v_1 yields: (5.000000, 25.000000)
Dot-product multiplication of v_1 * v_2 yields: 29.000000
Length Method A: the length of v_1 is 5.099020
Length Method B: the length of v_1 is 5.099020
The length of our 5-D vector is: 12.845233

Play with vectors

The interactive PYTHON game below lets you play with a vector, interacting by using sliders to change things like the total length of the vector or the lengths of its components. Consider the following critical questions:

  1. What happens to the direction of the total vector when you change its total length?
  2. What happens to the direction of the total vector when you change the length of either of its components?

In [23]:
from ipywidgets import widgets
from ipywidgets import interact
import matplotlib.pyplot as plt
import math
%matplotlib inline

def f(x):
    print(x)

global x_last
x_last = 0.0

global y_last
y_last = 0.0

def draw_vector(x,y):
    global x_last
    global y_last

    plt.axis('on')
    plt.xlim(-5.0,5.0)
    plt.ylim(-5.0,5.0)

    
    xlength = x
    ylength = y
    length = math.sqrt(x**2 + y**2)
    
    # Draw the previous vector in a fainter color/shade
    if (x_last != 0 and y_last != 0):
        length_last = math.sqrt(x_last**2 + y_last**2)
        plt.arrow(0, 0, x_last, y_last, head_width=0.2, head_length=0.2, fc='c', ec='c');
    
    # Draw the vector
    plt.arrow(0, 0, xlength, ylength, head_width=0.2, head_length=0.2, fc='k', ec='k');
    plt.show()
    
    x_last = x
    y_last = y

    
interact(draw_vector,x=widgets.IntSlider(min=-4,max=4,step=1,value=1),y=widgets.IntSlider(min=-4,max=4,step=1,value=1))

print("Note: the current vector is in black, while the previous vector is in cyan (light blue).")


Note: the current vector is in black, while the previous vector is in cyan (light blue).

Fun With Vectors - Moving About

Let us now program a simple "animation" by adding TIME as an additional dimension to the problem. The above graphic of a northeast-pointing vector is static - it represents a moment in frozen time. It never changes. Let's add time as a step, and have some fun with this by making something that moves.

Let us do this. Let us:

  • Define a "game board" of a fixed size.
  • Draw a vector on the game board that fits inside the board.
  • Wait a second, then draw a new vector on the game board that starts at the tip of the previous vector and points in the same direction as the original one. If the length of this vector exceeds the game board size, "bend" it to point back into the board. In essence, "bounce" it off the wall.
  • Keep this going. Draw a new vector. If it fits in the board, draw the next one in the same direction. If it doesn't, "bounce" it off the wall.

How do we do the "bounce"? Let's choose the following:

  • If the horizontal component of the vector exceeds the horizontal boundary, flip the horizontal component around and start the next vector on the "wall" of the board.
  • if the vertical component of the vector exceeds the vertical boundary, flip the vertical component around and again start the next vector on the wall.
  • If BOTH exceed the boundary, flip both and start on the wall.

You can make up your own rules. Here is what the program looks like in PYTHON if we implement these.

Note:

Try playing around with the total number of steps in the game ("total_steps") and the direction and length of the original vector (look for the comment, "initial vector," preceding those lines of code).


In [4]:
# Library setups
import numpy as np
import math
import matplotlib.pyplot as plt

# Define variables accessible (and modifiable) by functions
global board_length
board_length = 1

global board_height
board_height = 1

# colors that we can assign to the arrow and change in the game
global colors
colors=['k','b','g','r','c','m'] # black, blue, red, green, cyan, magenta

global vector_color
vector_color = 0

# Define some functions for determining if our vector is on the board
# A "function" takes input and does something to it. It allows you to
# encapsulate repetative tasks.

# Is the vector's front end inside the bounds of the board's horizontal size?
def vector_in_board_horizontally(x_start = 0, v = np.array([])):
    if (x_start + v[0]) < 0:
        return -1
    if (x_start + v[0]) > board_length:
        return 1
    return 0

# Is the vector's front end inside the bounds of the board's vertical size?
def vector_in_board_vertically(y_start = 0, v = np.array([])):
    if (y_start + v[1]) < 0:
        return -1
    if (y_start + v[1]) > board_length:
        return 1
    return 0

# Define some functions for flipping vector components
def flip_horizontal(v = np.array([])):
    return np.array([-v[0],v[1]])

def flip_vertical(v = np.array([])):
    return np.array([v[0],-v[1]])

# function to draw vector
def draw_vector(x_initial = 0.5, y_initial = 0.5, v = np.array([])):
    plt.arrow(x_initial, y_initial, v[0], v[1], head_width=0.05, head_length=0.1, fc=colors[vector_color], ec=colors[vector_color]);
    return v

def next_color():
    global vector_color
    vector_color += 1
    if vector_color >= len(colors):
        vector_color = 0
    return vector_color

# Draw the board and then play the game
# We'll need this more later, but this code snippet will draw arrows. We need them to represent the above statement
import matplotlib.pyplot as plt
import math
%matplotlib inline
plt.axis('on')
plt.xlim(0,board_length)
plt.ylim(0,board_height)


# initial vector
x_start = 0.32
y_start = 0.5
v = draw_vector(x_initial = x_start, y_initial = y_start, v=np.array([0.1,0.2]))

# Place a dot at the starting point - this helps us find the start point!
plt.plot(x_start,y_start, marker='o', color=colors[vector_color], ls='')


# Step the arrow around the board, applying our rules when a vector reaches the boundaries
# When we "bounce" off a wall, change the color!
total_steps = 50

# a "for loop" repeats a task over and over again, up to a limit that you define
for step in range(total_steps):
    v_next = v

    # update the start point
    x_start = x_start + v[0]
    y_start = y_start + v[1]
    
    horizontal_choice = vector_in_board_horizontally(x_start, v)
    if horizontal_choice == 0:
        v_next = v_next
    elif horizontal_choice == -1:
        v_next = flip_horizontal(v_next)
        x_start = 0
        next_color()
    elif horizontal_choice == 1:
        v_next = flip_horizontal(v_next)
        x_start = board_length
        next_color()
        
        
    vertical_choice = vector_in_board_vertically(y_start, v)
    if vertical_choice == 0:
        v_next = v_next
    elif vertical_choice == -1:
        v_next = flip_vertical(v_next)
        y_start = 0
        next_color()
    elif vertical_choice == 1:
        v_next = flip_vertical(v_next)
        y_start = board_length
        next_color()
  
    v = v_next
    draw_vector(x_start, y_start, v)



In [ ]: